<?php
/*
KAP 0.3 : fileManager

file version 2005-06-15 16:28:29
*/

class wakfileManager extends wakCoreComponent {
/*----------------------------------------------------------------------------------------------
                                                                                      proprietes
----------------------------------------------------------------------------------------------*/
	var $default_parameters=array(
		/*'textFormats'=>array('txt','xml','php','csv','sql')*/
		'mimeFromExtension'=>array( // types mime en fonction de l'extension ext => mime
			'asp'=>'text/asp'
			, 'avi'=>'video/avi'
			, 'bz'=>'application/x-bzip','bz2'=>'application/x-bzip'
			, 'conf'=>'text/plain'
			, 'css'=>'text/css'
			, 'doc'=>'application/msword'
			, 'dump'=>'application/octet-stream'
			, 'eps'=>'application/postscript'
			, 'exe'=>'application/octet-stream'
			, 'gif'=>'image/gif'
			, 'gz'=>'application/x-gzip','gzip'=>'application/x-gzip'
			, 'htm'=>'text/html','html'=>'text/html','xhtml'=>'text/html','shtml'=>'text/html'
			, 'ico'=>'image/x-icon','imap'=>'application/x-httpd-imap'
			, 'jfif'=>'image/jpeg','jpe'=>'image/jpeg','jpg'=>'image/jpeg','jpeg'=>'image/jpeg'
			, 'js'=>'application/x-javascript'
			, 'log'=>'text/plain'
			, 'lzh'=>'application/octet-stream','lzx'=>'application/octet-stream'
			, 'midi'=>'audio/midi'
			, 'mov'=>'video/quicktime'
			, 'mp3'=>'audio/mpeg3','mpe'=>'video/mpeg','mpeg'=>'video/mpeg','mpg'=>'video/mpeg'
			, 'pdf'=>'application/pdf'
			, 'php'=>'text/php','php3'=>'text/php','php4'=>'text/php','php5'=>'text/php'
			, 'psd'=>'application/octet-stream'
			, 'ra'=>'audio/x-pn-realaudio','ram'=>'audio/x-pn-realaudio'
			, 'sgm'=>'text/sgml','sgml'=>'text/sgml'
			, 'swf'=>'application/x-shockwave-flash'
			, 'txt'=>'text/plain','text'=>'text/plain'
			, 'tif'=>'image/tiff','tiff'=>'image/tiff'
			, 'xls'=>'application/excel'
			, 'xml'=>'text/xml'
		)
	);
/*----------------------------------------------------------------------------------------------
                                                                                    constructeur
----------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------
                                                                              methodes publiques
----------------------------------------------------------------------------------------------*/

	function getContent($file) {
		if (!$lines=@file($file)) {
			trigger_error("fileManager: failure to read $file",E_USER_WARNING);
			return false;
		}
		wakLog("fileManager: $file read");
		return join('',$lines);
	}
	
	function getMimeType($file) {
		if (function_exists('mime_content_type')) {
			return mime_content_type($file);
		} else {
			if (is_dir($file)) return ('dir');
			$ext=substr($file,strrpos($file,'.')+1);// echo $ext;
			// ereg('.*\.?([^.]+)?',$file,$found); $ext=$found[1]; 
			// echo "<pre>"; var_dump($found); echo "</pre>";
			if (isset($this->parameters['mimeFromExtension'][$ext]))
				return $this->parameters['mimeFromExtension'][$ext];
			else return false;
		}
	}
	
	// renvoie la liste des fichiers d'un chemin, en chemin absolu
	function listPath($path,$recurse=false, $ignoreDot=true) {
		if (is_file($path)) return array($path);
		if (is_dir($path)) {
			// coupure du slash final superflu si nécessaire
			if (substr($path,-1,1)=='/') $path=substr($path,0,-1);
			// listage des fichiers du dossier
			if (!$dir=opendir($path)) return false;
			$list=array();
			while (false !== ($file = readdir($dir))) {
				if ($file=="." or $file=="..")
					continue;
				if ($file[0]=='.' and $ignoreDot)
					continue;
				// coupue slash initial superflu
				//if (substr($file,0,1)=='/') $path=substr($file,1);
				// nom complet
				$filename=$path?$path.'/'.$file:$file;
				if (is_dir($file) and $recurse===true)
					$list=array_merge($this->listPath($filename,$recurse));
				else
					$list[]=$filename;
			}
			return $list;
		}
		return false;
	}
	
	/* retourne un tableau d'informations sur un fichier
	*/
	function getInfo($file) {
		if (!file_exists($file)) return false;
		return array(
			'name'=>$file
			, 'path'=>dirname($file)
			, 'basename'=>basename($file)
			, 'mtime'=>filemtime($file)
			, 'size'=>filesize($file)
			, 'readablesize'=>$this->readableSize(filesize($file))
			, 'mimetype'=>$this->getMimeType($file)
			, 'is_dir'=>is_dir($file)
			, 'is_file'=>is_file($file)
			, 'is_readable'=>is_readable($file)
			, 'is_writeable'=>is_writeable($file)
		);
	}
	
	/* recherche toutes les correspondances de l'expression régulière dans le contenu des fichiers 
	texte du chemin, renvoie un tableau de la forme :
	filename => array([array result], [array result])
	où chaque tableau result est le resultat de ereg(regex, contenu, result)*/
	function regexSearchPathContent($regex, $path="", $recurse=false,$around=false) {
		$list=$this->listPath($path,$recurse);// echo affvar($file);
		$results=array();
		if (is_array($list)) {
			foreach ($list as $file) {
				$type=$this->getMimeType($file);// echo "$file : $type";
				if (substr($type,0,4)!="text") continue;
				$result=$this->regexSearchContent($regex,$file,$around);
				// coupure du chemin initial ?
				if ($result) $results[$file]=$result;
			}
		}
		return $results;
	}
	
	/* recherche d'expressions régulières dans le contenu d'un fichier texte
	renvoie un tableau n => array(
		'fullstring'=>chaine complète,
		'captured'=>chaines capturées,
		'around'=>x caractères autour de la chaine si aroundChar est numérique)
	*/
	function regexSearchContent($regex, $file,$aroundChars=25) {
		/*if (!$type=$this->getMimeType($file)) return false; echo $type;
		if (substr($type,0,4)!="text") return false;*/
		// lecture du fichier
		if (!$content=$this->getContent($file)) return false;
		$results=array();
		//if (ereg($regex,$content,$found)) return $found;
		$n=0;
		while (ereg($regex,$content,$found)) {
			$fullstring=array_shift($found);
			$parts=$found;
			// recherche de l'environnement
			if ($aroundChars and ereg(".{1,$aroundChars}$regex.{1,$aroundChars}",$content,$found2))
				$around=$found2[0];
			else $around='';
			//echo $around.'<br />';
			$results[]=array('fullstring'=>$fullstring,'captured'=>$found,'around'=>$around);
			// suppression de l'ocurrence et du texte precedent dans le champ de recherche
			if (!$content=ereg_replace('.*'.$this->escapeRegexChars($fullstring),'',$content)) {
				// echo $content;
				break;
			}
			$n++; if ($n>50) break; // en cas de boucle infinie
		}
		return $results;
	}
	/* recherche les fichiers dont le nom correspond à l'expression régulière dans le chemin spécifié
	retourne un tableau indexé numériquement ou sur le resultat de la premiere parenthese capturante
	*/
	function regexSearchPathName($regex, $path="", $recurse=false) {
		// listage
		$pathFiles=$this->listpath($path,$recurse);
		$matches=array();
		foreach ($pathFiles as $filename) {
			// suppression du chemin spécifié du nom de fichier
			$filename=ereg_replace($path.'/?','',$filename);
			if (ereg($regex,$filename,$found)) {
				if (isset($found[1]) and $key=$found[1])
					// indexation sur la premiere capture
					$matches[$key]=$filename;
				else
					$matches[]=$filename;
			}
		}
		return $matches;
	}
	/* télécharge un fichier sur le serveur
	vérifie qu'il n'existe pas un fichier de même nom à l'emplacement de destination
	le cas échéant, compare les somme md5
	en cas de différence, renomme le fichier d'origine en *.YYYY-mm-dd_HH-ii-ss.bak
	renvoie true si le fichier a été téléchargé avec succès ou s'il est déjà present et identique */
	function safeUpload($postName,$path,$newName=NULL) {
		if (!$uploaded=$this->uploadedSrc($postName)) return false;
		$target=$this->uploadTarget($postName,$path,$newName);
		if (is_file($target)) {
			// est il identique ?
			//echo md5_file($target)."==".md5_file($uploaded);
			if (md5_file($target)==md5_file($uploaded)) return true;
			//echo filemtime($target)." / ".filemtime($uploaded);
			// si non, renommage du fichier existant
			if (!$this->backup($target)) {
				trigger_error('echec de renommage',E_USER_NOTICE);
				return false;
			}
		}
		return move_uploaded_file($uploaded,$target);
	}
	/* idem que la fonction précedente
	mais conserve le nom de fichier et recherche un chaine de version "version yyyy-mm-dd hh:ii:ss"
	et compare cette chaine avec le fichier déjà présent le cas échéant
	et conserve le fichier existant s'il est plus récent
	*/
	function versionUpload($postName,$path) {
		if (!$uploaded=$this->uploadedSrc($postName)) return false;
		$target=$this->uploadTarget($postName,$path);
		if (is_file($target)) {
			// est il identique ?
			//echo md5_file($target)."==".md5_file($uploaded);
			if (md5_file($target)==md5_file($uploaded)) return true;
			// recherche des chaines de version
			if (!$upVersion=$this->searchVersion($uploaded)) return false;
			if (!$targetVersion=$this->searchVersion($target)) {
				// on prend la date d'upload
				//$modified=; echo $modified;
				$targetVersion=date('Y-m-d H:i:s',filemtime($target));
			}
			// comparaison des versions, abandon si la version existante est la plus récente
			if ($targetVersion>=$upVersion) return false;
			// si non, renommage du fichier existant
			if (!$this->backup($target)) {
				trigger_error('echec de renommage',E_USER_NOTICE);
				return false;
			}
		}
		return move_uploaded_file($uploaded,$target);
	}
/*----------------------------------------------------------------------------------------------
                                                                                methodes privées
----------------------------------------------------------------------------------------------*/
	function escapeRegexChars($regex) {
		$regex=str_replace('(','\(',$regex);
		$regex=str_replace(')','\)',$regex);
		$regex=str_replace('[','\[',$regex);
		$regex=str_replace('.','\.',$regex);
		$regex=str_replace('*','\*',$regex);
		$regex=str_replace('+','\+',$regex);
		
		return $regex;
	}
	
	function backup($file) {
		return rename($file,$file.".".date('Y-m-d_H-i-s').'.bak');
	}
	
	/*function compare($file1,$file2) {
	
	}*/
	// renvoie le chemin du fichier téléchargé le cas échéant
	function uploadedSrc($postName) {
		if (!isset($_FILES[$postName])) return false;
		return $_FILES[$postName]['tmp_name'];
	}
	
	function uploadTarget($postName,$path,$newName=NULL) {
		if (!isset($_FILES[$postName])) return false;
		if ($newName===NULL) $newName=$_FILES[$postName]['name'];
		if (substr($path,-1,1)!='/') $path.='/';
		return $path.$newName;
	}
	
	function searchVersion($file) {
		//if (substr($this->getMimeType($file),0,4)!="text") return false;
		$year="[0-9]{4}"; $mon="((0[1-9])|(1[0-2]))"; $day="((0[1-9])|([1-2][0-9])|(3[0-1]))";
		$hour="(([0-1][0-9])|(2[0-3]))"; $min="[0-5][0-9]";
		$regex="file version (".$year."-".$mon."-"."$day $hour([:]$min){2})";// echo $regex;
		$results=$this->regexSearchContent($regex,$file);// echo affvar($results);
		if (!$results) return false;
		return $results[0]['captured'][0];
	}
	
	// retourne une taille lisible avec adaptation de l'unité en fonction de la taille
	function readableSize($size) {
		if ($size>1048576) return sprintf('%.2f',$size/1048576)." Mo";
		if ($size>1024) return sprintf('%.2f',$size/1024)." Ko";
		return $size." octets";
	}
}
?>
